package com.tsd.core.openweixin;


import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.tsd.core.openweixin.model.*;
import com.tsd.core.openweixin.model.material.*;
import com.tsd.core.openweixin.utils.WechatUtils;
import com.tsd.core.utils.ExceptionUtil;
import com.tsd.core.utils.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 微信客户端，封装获取AccessToken、获取用户信息、发送消息、创建菜单、生成二维码等功能
 * <a href="http://mp.weixin.qq.com/wiki/home/index.html">微信官方文档</a>
 *
 * @author eric
 */
public class WechatClient {

    /**
     * 发送消息的URL
     */
    public static final String SEND_CUSTOM_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
    /**
     * 发送模板消息的URL
     */
    public static final String SEND_TEMPLATE_MSG_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send";
    /**
     * 增加永久素材的URL
     */
    public static final String ADD_MATERIAL_URL = "https://api.weixin.qq.com/cgi-bin/material/add_material";
    /**
     * 上传临时素材的URL
     */
    public static final String UPLOAD_MATERAIL_URL = "https://api.weixin.qq.com/cgi-bin/media/upload";
    /**
     * 创建自定义菜单的URL
     */
    public static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create";
    /**
     * 删除素材的URL
     */
    public static final String DELETE_MATERIAL_URL = "https://api.weixin.qq.com/cgi-bin/material/del_material";
    /**
     * 获取素材的URL
     */
    public static final String GET_MATERIAL_URL = "https://api.weixin.qq.com/cgi-bin/material/get_material";
    /**
     * 生成二维码URL
     */
    public static final String CREATE_QR_CODE_URL = "https://api.weixin.qq.com/cgi-bin/qrcode/create";
    /**
     * 根据OpenId获取微信用户信息的URL
     */
    public static final String GET_WX_USER_INFO_BY_OPEN_ID_URL = "https://api.weixin.qq.com/cgi-bin/user/info";
    /**
     * 统一支付接口
     */
    public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//统一支付接口
    /**
     * 群发接口
     */
    public static final String URL_MASS_SEND_ALL = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall";
    /**
     * 群发接口(发送到指定的openId)
     */
    public static final String URL_MASS_SEND_OPEN_ID_LIST = "https://api.weixin.qq.com/cgi-bin/message/mass/send";
    private static final Logger LOGGER = LoggerFactory.getLogger(WechatClient.class);
    private static final TypeAdapter<Boolean> BOOLEAN_AS_INT_ADAPTER = new TypeAdapter<Boolean>() {
        @Override
        public void write(JsonWriter out, Boolean value) throws IOException {
            if (value == null) {
                out.nullValue();
            } else {
                out.value(value);
            }
        }

        @Override
        public Boolean read(JsonReader in) throws IOException {
            JsonToken peek = in.peek();
            switch (peek) {
                case BOOLEAN:
                    return in.nextBoolean();
                case NULL:
                    in.nextNull();
                    return null;
                case NUMBER:
                    return in.nextInt() != 0;
                case STRING:
                    return Boolean.parseBoolean(in.nextString());
                default:
                    throw new IllegalStateException("Expected BOOLEAN or NUMBER but was " + peek);
            }
        }
    };
    public static Map<String, AccessToken> tokenMap = new HashMap<String, AccessToken>();
    public static Map<String, JsapiTicket> ticketMap = new HashMap<String, JsapiTicket>();
    private final String appid, secret;
    private final boolean isWeixinApp;
    private final Gson gson;
    TokenService tokenService;

    /**
     * 构建WeChatClient
     *
     * @param appid
     * @param secret
     * @param isWeixinApp 是否微信应用，如果不是，则token需要通过网络访问微信app获取token
     */
    public WechatClient(String appid, String secret, boolean isWeixinApp) {
        this.appid = appid;
        this.secret = secret;
        this.isWeixinApp = isWeixinApp;
        gson = new GsonBuilder()
                .registerTypeAdapter(Boolean.class, BOOLEAN_AS_INT_ADAPTER)
                .registerTypeAdapter(boolean.class, BOOLEAN_AS_INT_ADAPTER)
                .create();
    }
//    /**
//     * 构建WeChatClient
//     * @param appid
//     * @param secret
//     * @param token
//     */
//    public WechatClient(String appid, String secret,String token) {
//        this.appid = appid;
//        this.secret = secret;
//        gson = new GsonBuilder()
//                .registerTypeAdapter(Boolean.class, booleanAsIntAdapter)
//                .registerTypeAdapter(boolean.class, booleanAsIntAdapter)
//                .create();
//    }

    public static void main(String[] args) {
//		WechatClient wx = new WechatClient("wxc34426e16d4e2945","e902f5cb4de1f12f4987aa5cb8fd3108");
//		WechatMessageResult4Media ss = wx.uploadMaterail("voice", "d:/tmp/2015-05-26 16-02-44.mp3");
//
//		System.out.print("getMedia_id:"+ss.getMedia_id());
//		String s = getTemplateMsg();
//		System.out.println(s);
//		String token="VbZIM-0jlzWnHo7B221p7GpzrhbwPLYMGNN2wj8Csc_fLm8VlK6oe52bQyOLEzjuF5-TTnOvqKgR3z1esQwd0sw3Mb_mD9PERfUS_B7KHx0";
//		WxMaterialCount c = wx.getMaterialCount(token);
//		System.out.print(c.toString());
//		WechatMessageResult4Media rm = wx.addMaterial("image", "d:/upload2.png.txt",token);
//		System.out.print(rm.isSuccess());
//		WxMaterialList list = wx.getMaterialList("image", 0, 20, token);
//		System.out.print(list.getItem());
//		try {
//			int c = 0;
//			for (WxMaterialItem i:list.getItem()){
//				c++;
//				String mediaId = i.getMedia_id();
//				byte[] buf = wx.getMaterial(mediaId, token);
//				FileUtil.write(buf,"d:/wx/"+c+"-"+i.getName());
//			}
//		} catch (IOException e) {
//			e.printStackTrace();
//		}

//		WechatMessageResult ret = wx.deleteMaterailNews("zfO10fdOZeQueCvQHdMqPwnxtGP_Fp_2B2pL3cRC4eE", token);
//		System.out.print(ret.isSuccess());

//		zfO10fdOZeQueCvQHdMqPwnxtGP_Fp_2B2pL3cRC4eE
//		WxMaterialNewsList list= wx.getMateriaNewslList( 0, 1, token);
//		System.out.print(list.isSuccess());

//		long l = 1431007448l;
//		Date d = new Date(l*1000);
//		System.out.println(d);
//		t=0, image_count =53, news_count=9, video_count=0}
    }

    /**
     * 获取AccessToken
     * 处理过程：如果直接获取的为空或者已经超时，则更新AccessToken，否则直接返回
     *
     * @return
     */
    public synchronized AccessToken getAccessToken() {
        AccessToken accessToken = tokenMap.get(appid);
        long now = new Date().getTime();
        if (accessToken == null || now > accessToken.getExpiresTimestemp()) {
            if (isWeixinApp) {
                accessToken = WechatUtils.getAccessToken(appid, secret);
            } else {
                if (tokenService != null) {
                    accessToken = tokenService.getToken();
                }
            }
            tokenMap.put(appid, accessToken);
        }
        return accessToken;
    }

    /**
     * 获取JsapiTicket
     * 处理过程：如果直接获取的为空或者已经超时，则更新JsapiTicket，否则直接返回
     *
     * @return
     */
    public synchronized JsapiTicket getJsapiTicket() {
        JsapiTicket ticket = ticketMap.get(appid);
        long now = new Date().getTime();
        if (ticket == null || now > ticket.getExpiresTimestemp()) {
            AccessToken accessToken = tokenMap.get(appid);
            if (tokenService != null) {
                ticket = tokenService.getJsapiTicket();
            } else {
                ticket = WechatUtils.getJsapiTicket(accessToken.getToken());
            }
            ticketMap.put(appid, ticket);
        }
        return ticket;
    }

    /**
     * 发送微信客服消息
     *
     * @param messageJson json格式的消息
     * @return
     */
    public WechatMessageResult sendCustomMessage(String messageJson) {
        ClientConfig config = new DefaultClientConfig();
        Client client = Client.create(config);
        WebResource webResource = client.resource(SEND_CUSTOM_MESSAGE_URL);
        ClientResponse clientResponse = webResource
                .queryParam("access_token", getAccessToken().getToken())
                .post(ClientResponse.class, messageJson);

        if (clientResponse.getStatus() != 200) {
            throw new IllegalStateException("status error:" + clientResponse.getStatus());
        } else {
            return new Gson().fromJson(clientResponse.getEntity(String.class), WechatMessageResult.class);
        }
    }

    /**
     * 增加模板
     * 从行业模板库选择模板到账号后台，获得模板ID的过程可在MP中完成
     *
     * @return
     */
    public WechatMessageResult4Template addTemplateMsg(String template_id_short) {
        ClientConfig config = new DefaultClientConfig();
        Client client = Client.create(config);
        WebResource webResource = client.resource("https://api.weixin.qq.com/cgi-bin/template/api_add_template");
        ClientResponse clientResponse = webResource
                .queryParam("access_token", getAccessToken().getToken())
                .post(ClientResponse.class, "{\"template_id_short\":\"" + template_id_short + "\"");

        if (clientResponse.getStatus() != 200) {
            throw new IllegalStateException("status error:" + clientResponse.getStatus());
        } else {
            return new Gson().fromJson(clientResponse.getEntity(String.class), WechatMessageResult4Template.class);
        }
    }

    /**
     * 发送微信模板消息
     *
     * @param messageJson json格式的消息
     * @return
     */
    public WechatMessageResult sendTemplateMsg(String messageJson) {
        ClientConfig config = new DefaultClientConfig();
        Client client = Client.create(config);
        WebResource webResource = client.resource(SEND_TEMPLATE_MSG_URL);
        ClientResponse clientResponse = webResource
                .queryParam("access_token", getAccessToken().getToken())
                .post(ClientResponse.class, messageJson);

        if (clientResponse.getStatus() != 200) {
            throw new IllegalStateException("status error:" + clientResponse.getStatus());
        } else {
            return new Gson().fromJson(clientResponse.getEntity(String.class), WechatMessageResult.class);
        }
    }

    /**
     * 发送微信模板消息,返回的是字符串
     *
     * @param messageJson json格式的消息
     * @return
     */
    public String sendTemplateMsgReturnString(String messageJson) {
        ClientConfig config = new DefaultClientConfig();
        Client client = Client.create(config);
        WebResource webResource = client.resource(SEND_TEMPLATE_MSG_URL);
        ClientResponse clientResponse = webResource
                .queryParam("access_token", getAccessToken().getToken())
                .post(ClientResponse.class, messageJson);

        if (clientResponse.getStatus() != 200) {
            WechatMessageResult o = new WechatMessageResult();
            o.setErrorCode("status error:" + clientResponse.getStatus());
            o.setErrorMessage("http response status is not 200");
            return JSON.toJSONString(o);
        } else {
            return clientResponse.getEntity(String.class);
        }
    }

    /**
     * 群发图文消息
     *
     * @param mediaId mediaId
     * @return
     */
    public WechatMessageResult4SendAll sendAllNews(String mediaId) {
        String messageJson = "{\"filter\":{\"is_to_all\":false},\"mpnews\":{\"media_id\":\"" + mediaId + "\"},\"msgtype\":\"mpnews\"}";
//    	String messageJson="{\"touser\":[\"oEKqntzB0ttjs-cvwkqHmABfPr_M\",\"openId1\"],\"mpnews\":{\"media_id\":\""+mediaId+"\"},\"msgtype\":\"mpnews\"}";
        ClientConfig config = new DefaultClientConfig();
        Client client = Client.create(config);
        WebResource webResource = client.resource(URL_MASS_SEND_ALL);
//        WebResource webResource = client.resource(URL_MassSendOpenIdList);
        ClientResponse clientResponse = webResource
                .queryParam("access_token", getAccessToken().getToken())
                .post(ClientResponse.class, messageJson);

        if (clientResponse.getStatus() != 200) {
            throw new IllegalStateException("status error:" + clientResponse.getStatus());
        } else {
            return new Gson().fromJson(clientResponse.getEntity(String.class), WechatMessageResult4SendAll.class);
        }
    }
//    /**
//     * 增加永久素材,返回JSON字符串
//     * @param type 类型 ：图片（image）、语音（voice）
//     * @param filePath
//     * @return
//     */
//    public String addMaterialJSON(String type,String filePath,String token) {
//    	Map<String, String> params = new LinkedHashMap<String, String>();
//    	params.put("type",type);  
//        params.put("access_token", token==null?getAccessToken().getToken():token);
//        Map<String, String> filePaths = new LinkedHashMap<String, String>();
//        filePaths.put("media", filePath);
//        String result =null;
//		try {
//			result = HttpUtil.postMultipartUrl(AddMaterialURL, params, filePaths, "UTF-8");
//		} catch (ClientProtocolException e) {
//			e.printStackTrace();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		return result;
//    }

    /**
     * 增加永久素材
     *
     * @param type     类型 ：图片（image）、语音（voice）
     * @param filePath
     * @return
     */
    public WechatMessageResult4Media addMaterial(String type, String filePath, String token) {
        String result = null;//addMaterialJSON(type, filePath, token);
        if (result != null) {
            return JSON.parseObject(result, WechatMessageResult4Media.class);
        } else {
            return null;
        }
    }

    /**
     * 增加永久性图文素材
     *
     * @param m     图文素材
     * @param token 令牌，如果为空则从context获取
     * @return
     */
    public WechatMessageResult4Media addMaterialNews(WxMaterialNews4Add m, String token) {

        try {
            String result2 = addMaterialNewsJSON(m, token);
            return JSON.parseObject(result2, WechatMessageResult4Media.class);
        } catch (Exception e) {
            LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            return null;
        }
    }

    /**
     * 增加永久性图文素材 ,返回JSON字符串
     *
     * @param m     图文素材
     * @param token 令牌，如果为空则从context获取
     * @return
     */
    public String addMaterialNewsJSON(WxMaterialNews4Add m, String token) {
        String url = WxMaterialNews4Add.serverUrl + "?access_token=" + (token == null ? getAccessToken().getToken() : token);
        try {
            byte[] b = HttpUtil.postUrlRawJSON(url, JSON.toJSONString(m), "UTF-8");
            return new String(b, StandardCharsets.UTF_8);
        } catch (Exception e) {
            LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            return null;
        }
    }

    /**
     * 修改永久性图文素材
     *
     * @param m     图文素材
     * @param token 令牌，如果为空则从context获取
     * @return
     */
    public WechatMessageResult updateMaterailNews(WxMaterialNews4Update m, String token) {
        String url = WxMaterialNews4Update.serverUrl + "?access_token=" + (token == null ? getAccessToken().getToken() : token);
        try {
            byte[] b = HttpUtil.postUrlRawJSON(url, JSON.toJSONString(m), "UTF-8");
            String result2 = new String(b, StandardCharsets.UTF_8);
            return JSON.parseObject(result2, WechatMessageResult.class);
        } catch (Exception e) {
            LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            return null;
        }
    }

    /**
     * 删除素材
     *
     * @param mediaId
     * @param token
     * @return
     */
    public WechatMessageResult deleteMaterail(String mediaId, String token) {
        String url = DELETE_MATERIAL_URL + "?access_token=" + (token == null ? getAccessToken().getToken() : token);
        try {
            Map<String, String> params = new LinkedHashMap<String, String>();
            params.put("media_id", mediaId);
            byte[] ret = HttpUtil.postUrlRawJSON(url, JSON.toJSONString(params), "UTF-8");
            String s = new String(ret, StandardCharsets.UTF_8);
            return JSON.parseObject(s, WechatMessageResult.class);
        } catch (Exception e) {
            LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            return null;
        }
    }
//    /**
//     * 增加临时素材
//     * @param type 类型 ：图片（image）、语音（voice）
//     * @param filePath
//     * @return
//     */
//    public  WechatMessageResult4Media uploadMaterail(String type,String filePath) {
//    	Map<String, String> params = new LinkedHashMap<String, String>();
//    	params.put("type", type);  
//        params.put("access_token", getAccessToken().getToken());
//        Map<String, String> filePaths = new LinkedHashMap<String, String>();
//        filePaths.put("media", filePath);
//        String result =null;
//		try {
//			result = HttpUtil.postMultipartUrl(UploadMaterailURL, params, filePaths, "UTF-8");
//		} catch (ClientProtocolException e) {
//			e.printStackTrace();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		return JSON.parseObject(result,WechatMessageResult4Media.class);
//    }

    /**
     * 根据media_id来获取永久素材，返回的是字节数组，根据不同的素材类型再确定如何处理，具体如下：
     * 如果请求的素材为图文消息，返回对应的WxMaterialNewsContent对象
     * 如果请求的素材是视频，返回对应的是WxMaterialVideo对象
     * 如果是其他类型的素材，则响应的直接为素材的内容，开发者可以自行保存为文件
     *
     * @param mediaId
     * @param token
     * @return
     */
    public byte[] getMaterial(String mediaId, String token) {
        String url = GET_MATERIAL_URL + "?access_token=" + (token == null ? getAccessToken().getToken() : token);
        try {
            Map<String, String> params = new LinkedHashMap<String, String>();
            params.put("media_id", mediaId);
            return HttpUtil.postUrlRawJSON(url, JSON.toJSONString(params), "UTF-8");
        } catch (Exception e) {
            LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            return null;
        }
    }

    /**
     * 获取素材总数量
     *
     * @return
     */
    public WxMaterialCount getMaterialCount(String token) {
        String result;
        try {
            result = getMaterialCountJson(token);
            return JSON.parseObject(result, WxMaterialCount.class);
        } catch (Exception e) {
            LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            return null;
        }
    }

    /**
     * 获取素材总数量的json字符串
     *
     * @return
     */
    public String getMaterialCountJson(String token) {
        Map<String, String> params = new LinkedHashMap<String, String>();
        params.put("access_token", token == null ? getAccessToken().getToken() : token);
        String result;
        try {
            result = HttpUtil.postUrlSimple(WxMaterialCount.serverUrl, params, "UTF-8");
            return result;
        } catch (Exception e) {
            LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            return null;
        }
    }

    /**
     * 获取素材列表
     *
     * @param type   必须 素材的类型，图片（image）、视频（video）、语音 （voice）、图文（news）
     * @param offset 必须 从全部素材的该偏移位置开始返回，0表示从第一个素材 返回
     * @param count  必须 返回素材的数量，取值在1到20之间
     * @return
     */
    public WxMaterialList getMaterialList(String type, Integer offset, Integer count, String token) {
        String result;
        try {
            result = getMaterialListJSON(type, offset, count, token);
            return JSON.parseObject(result, WxMaterialList.class);
        } catch (Exception e) {
            LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            return null;
        }
    }

    /**
     * 获取素材列表的json字符串
     *
     * @param type
     * @param offset
     * @param count
     * @param token
     * @return
     */
    public String getMaterialListJSON(String type, Integer offset, Integer count, String token) {
        Map<String, Object> params = new LinkedHashMap<String, Object>();
        String url = WxMaterialList.serverUrl + "?access_token=" + (token == null ? getAccessToken().getToken() : token);
        params.put("type", type);
        params.put("offset", offset);
        params.put("count", count);
        String result;
        try {
            byte[] b = HttpUtil.postUrlRawJSON(url, JSON.toJSONString(params), "UTF-8");

            result = new String(b, StandardCharsets.UTF_8);
            return result;
        } catch (Exception e) {
            LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            return null;
        }
    }

    /**
     * 获取图文素材列表
     *
     * @param offset
     * @param count
     * @param token
     * @return
     */
    public WxMaterialNewsList getMateriaNewslList(Integer offset, Integer count, String token) {
        String result;
        try {
            result = getMaterialListJSON("news", offset, count, token);

            //System.out.println("@@@@@@@@@@@@@@@@@@"+result);
            return JSON.parseObject(result, WxMaterialNewsList.class);
        } catch (Exception e) {
            LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            return null;
        }
    }

    /**
     * 生成二维码
     *
     * @param expireSeconds
     * @param actionName
     * @param sceneId
     * @return
     */
    public WechatQRCode qrCodeCreate(Integer expireSeconds, QRCodeCreateActionName actionName, int sceneId) {
        if (QRCodeCreateActionName.QR_SCENE == actionName) {
            if (expireSeconds == null || expireSeconds < 1 || expireSeconds > 1800) {
                throw new IllegalArgumentException("expireSeconds must between 1 to 1800");
            }
        }

        if (sceneId < 1 || sceneId > 100000) {
            throw new IllegalArgumentException("sceneId must between 1 to 100000");
        }

        ClientConfig config = new DefaultClientConfig();
        Client client = Client.create(config);
        WebResource webResource = client.resource(CREATE_QR_CODE_URL);
        ClientResponse clientResponse;
        Map data = new QRCodeBuilder(actionName).expireSeconds(expireSeconds).sceneId(sceneId).bulid();
        LOGGER.debug("QRCode Post:{}", data.toString());
        clientResponse = webResource
                .queryParam("access_token", getAccessToken().getToken())
                .post(ClientResponse.class, new Gson().toJson(data));
        if (clientResponse.getStatus() != 200) {
            throw new IllegalStateException("status error:" + clientResponse.getStatus());
        } else {
            return gson.fromJson(clientResponse.getEntity(String.class), WechatQRCode.class);
        }
        //{"ticket":"gQG28DoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL0FuWC1DNmZuVEhvMVp4NDNMRnNRAAIEesLvUQMECAcAAA==","expire_seconds":1800}
        //{"errcode":40013,"errmsg":"invalid appid"}
    }

    /**
     * 根据openId获取微信用户信息
     *
     * @param openid
     * @return
     */
    public WechatUser getUserInfo(String openid) {
        ClientConfig config = new DefaultClientConfig();
        Client client = Client.create(config);
        WebResource webResource = client.resource(GET_WX_USER_INFO_BY_OPEN_ID_URL);
        //?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
        String token = getAccessToken().getToken();
        ClientResponse clientResponse = webResource
                .queryParam("access_token", token)
                .queryParam("openid", openid)
                .get(ClientResponse.class);
        if (clientResponse.getStatus() != 200) {
            throw new IllegalStateException("status error:" + clientResponse.getStatus());
        } else {
            String s = clientResponse.getEntity(String.class);
            return gson.fromJson(s, WechatUser.class);
        }
    }

    public String getPrePayId(String orderRequest) {
        ClientConfig config = new DefaultClientConfig();
        Client client = Client.create(config);
        WebResource webResource = client.resource(UNIFIED_ORDER_URL);
//        String token = getAccessToken().getToken();
        ClientResponse clientResponse = webResource
                .post(ClientResponse.class, orderRequest);
        if (clientResponse.getStatus() != 200) {
            throw new IllegalStateException("status error:" + clientResponse.getStatus());
        } else {
            return clientResponse.getEntity(String.class);
        }
    }

    /**
     * 创建菜单
     *
     * @param json
     * @return
     */
    public WechatMessageResult createMenu(String json) {
        ClientConfig config = new DefaultClientConfig();
        Client client = Client.create(config);
        WebResource webResource = client.resource(CREATE_MENU_URL);
        String token = getAccessToken().getToken();
        ClientResponse clientResponse = webResource
                .queryParam("access_token", token)
                .post(ClientResponse.class, json);
        if (clientResponse.getStatus() != 200) {
            throw new IllegalStateException("status error:" + clientResponse.getStatus());
        } else {
            String s = clientResponse.getEntity(String.class);
            WechatMessageResult ret = gson.fromJson(s, WechatMessageResult.class);
            if (ret != null && ("40001".equals(ret.getErrorCode()) || "40014".equals(ret.getErrorCode()))) {
                tokenMap.remove(appid);
                getAccessToken().getToken();
            }
            return ret;
        }
    }

    public TokenService getTokenService() {
        return tokenService;
    }

    public void setTokenService(TokenService tokenService) {
        this.tokenService = tokenService;
    }


    public interface TokenService {
        AccessToken getToken();

        JsapiTicket getJsapiTicket();
    }

    private static class QRCodeBuilder {

        private final Map<String, Object> qrCode = new HashMap<>();

        public QRCodeBuilder(QRCodeCreateActionName actionName) {
            qrCode.put("action_name", actionName.getValue());
        }

        private QRCodeBuilder expireSeconds(Integer expireSeconds) {
            if (expireSeconds != null) {
                qrCode.put("expire_seconds", expireSeconds);
            }
            return this;
        }

        @SuppressWarnings("unchecked")
        private QRCodeBuilder sceneId(int sceneId) {
            Map<String, Object> actionInfo = (Map<String, Object>) qrCode.get("action_info");
            if (actionInfo == null) {
                actionInfo = new HashMap<>();
                qrCode.put("action_info", actionInfo);
            }
            Map<String, Object> scene = (Map<String, Object>) actionInfo.get("scene");
            if (scene == null) {
                scene = new HashMap<>();
                actionInfo.put("scene", scene);
            }
            scene.put("scene_id", sceneId);
            return this;
        }

        private Map<String, Object> bulid() {
            return qrCode;
        }

    }

    public static class MassageBuilder {

        private final Map<String, Object> message = new HashMap<>();

        public MassageBuilder(String type) {
            message.put("msgtype", type);
        }

        public MassageBuilder toUser(String user) {
            message.put("touser", user);
            return this;
        }

        @SuppressWarnings("unchecked")
        public MassageBuilder textContent(String content) {
            Map<String, Object> text = (Map<String, Object>) message.get("text");
            if (text == null) {
                text = new HashMap<>();
                message.put("text", text);
            }
            text.put("content", content);
            return this;
        }

        public Map<String, Object> bulid() {
            LOGGER.debug("build message {}", message);
            return message;
        }

    }
}
